package top.z.orm.handle.logging;

import top.z.orm.core.logging.Log;
import top.z.orm.core.osql.Op;
import top.z.orm.handle.osql.core.HoneyConfig;

import java.lang.reflect.Constructor;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;

public class LoggerFactory {

    private static final ThreadLocal<Map<String, Log>> threadLocal;

    private static Constructor<? extends Log> logConstructor;

    private static Constructor<? extends Log> logNoArgConstructor;

    private static boolean isNoArgInConstructor;

    static {
        threadLocal = new ThreadLocal<>();
        String loggerType = HoneyConfig.getHoneyConfig().loggerType;
        if (loggerType != null && !"".equals(loggerType.trim())) {
            loggerType = loggerType.trim();
            if (loggerType.equalsIgnoreCase("log4j")) {
                boolean f = tryImplementation("org.apache.log4j.Logger", "org.teasoft.beex.logging.Log4jImpl"); //优先选择log4j
                if (!f) System.err.println("[Bee] [WARN] the loggerType: " + loggerType + " , set unsuccessfully! Maybe do not set the jar!");
            } else if (loggerType.equalsIgnoreCase("slf4j")) {
                boolean f = tryImplementation("org.slf4j.Logger", "org.teasoft.beex.logging.Slf4jImpl"); //ok,只是要显示多层
                if (!f) System.err.println("[Bee] [WARN] the loggerType: " + loggerType + " , set unsuccessfully! Maybe do not set the jar!");
            } else if (loggerType.equalsIgnoreCase("log4j2")) {
                boolean f = tryImplementation("org.apache.logging.log4j.Logger", "org.teasoft.beex.logging.Log4j2Impl"); //Log4j2
                if (!f) System.err.println("[Bee] [WARN] the loggerType: " + loggerType + " , set unsuccessfully! Maybe do not set the jar!");
            } else if (loggerType.equalsIgnoreCase("systemLogger")) {//std
                boolean f = tryImplementation("", "org.teasoft.honey.logging.SystemLogger");
                if (!f) System.err.println("[Bee] [WARN] the loggerType: " + loggerType + " , set unsuccessfully!");
            } else if (loggerType.equalsIgnoreCase("fileLogger")) {
                boolean f = tryImplementation("", "org.teasoft.honey.logging.FileLogger");
                if (!f) System.err.println("[Bee] [WARN] the loggerType: " + loggerType + " , set unsuccessfully!");
            } else if (loggerType.equalsIgnoreCase("noLogging")) {
                boolean f = tryImplementation("", "org.teasoft.honey.logging.NoLogging");
                if (!f) System.err.println("[Bee] [WARN] the loggerType: " + loggerType + " , set unsuccessfully!");
            } else if (loggerType.equalsIgnoreCase("jdkLog")) {
                boolean f = tryImplementation("java.util.logging.Logger", "org.teasoft.honey.logging.Jdk14LoggingImpl");//会随着传入的class变化.无行数输出
                if (!f) System.err.println("[Bee] [WARN] the loggerType: " + loggerType + " , set unsuccessfully!");
            } else if (loggerType.equalsIgnoreCase("commonsLog")) {
                boolean f = tryImplementation("org.apache.commons.logging.LogFactory", "org.teasoft.beex.logging.JakartaCommonsLoggingImpl");//无法显示调用类的信息
                if (!f) System.err.println("[Bee] [WARN] the loggerType: " + loggerType + " , set unsuccessfully! Maybe do not set the jar!");
            }
        }
        tryImplementation("org.apache.log4j.Logger", "org.teasoft.beex.logging.Log4jImpl"); //优先选择log4j
        tryImplementation("org.slf4j.Logger", "org.teasoft.beex.logging.Slf4jImpl"); //ok,只是要显示多层
        tryImplementation("org.apache.logging.log4j.Logger", "org.teasoft.beex.logging.Log4j2Impl"); //Log4j2
        tryImplementation("", "org.teasoft.honey.logging.SystemLogger");
        tryImplementation("", "org.teasoft.honey.logging.FileLogger");
        tryImplementation("", "org.teasoft.honey.logging.NoLogging");
        tryImplementation("java.util.logging.Logger", "org.teasoft.honey.logging.Jdk14LoggingImpl");//会随着传入的class变化.无行数输出
        tryImplementation("org.apache.commons.logging.LogFactory", "org.teasoft.beex.logging.JakartaCommonsLoggingImpl");//无法显示调用类的信息,设置了caller也没用
    }

    private static boolean tryImplement(String testClassName, String implClassName) {
        if (logConstructor != null) {
            return true;
        }
        if (isNoArgInConstructor && logNoArgConstructor != null) {
            return true;
        }
        try {
            if (implClassName != null) {
                // 这几种类型的构造函数, 可以不需要参数
                if (implClassName.endsWith(".Log4jImpl") || implClassName.endsWith(".Slf4jImpl") || implClassName.endsWith(".SystemLogger") || implClassName.endsWith(".NoLogging") || implClassName.endsWith(".FileLogger")) {
                    //测试是否存在.如果不存在,则跳到catch
                    if (testClassName != null && !"".equals(testClassName)) {
                        genClassByName(testClassName);
                    }
                    Class<?> implClassNoArg = genClassByName(implClassName);
                    logNoArgConstructor = implClassNoArg.getConstructor();
                    isNoArgInConstructor = true;
                    System.out.println("[Bee] LoggerFactory Use the Logger is : " + implClassName);
                    return true;
                }
            }
            if (testClassName != null && !"".equals(testClassName)) genClassByName(testClassName); //测试是否存在
            Class implClass = genClassByName(implClassName);
            logConstructor = implClass.getConstructor(new Class[]{String.class});

            System.out.println("[Bee] LoggerFactory Use the Logger is : " + implClassName);
            return true;
        } catch (NoSuchMethodException | ClassNotFoundException e) {
            e.printStackTrace();
            return false;
        }
    }

    public static Log getLog() {

        Log cacheLog = getCacheInfo("NoArg");
        if (cacheLog != null) return cacheLog;

        Log log = null;
        try {
            if (isNoArgInConstructor) {
                log = logNoArgConstructor.newInstance();
                setCacheInfo("NoArg", log);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        if (log != null) return log;

        log = getLog(LoggerFactory.class.getName());

        return log;
    }

    @SuppressWarnings("rawtypes")
    public static Log getLog(Class clazz) {
        return getLog(clazz.getName());
    }

    public static Log getLog(String loggerName) {
        if (loggerName == null || "".equals(loggerName.trim())) loggerName = LoggerFactory.class.getName();

        Log cacheLog = getCacheInfo(loggerName);
        if (cacheLog != null) return cacheLog;

        try {
            Log log = logConstructor.newInstance(loggerName);
            setCacheInfo(loggerName, log);
            return log;
        } catch (Throwable t) {
            throw new RuntimeException("Error creating logger'" + loggerName + "'.  Cause by: " + t, t);
        }
    }

    private static Class<?> genClassByName(String className) throws ClassNotFoundException {
        Class<?> clazz = null;
        try {
            clazz = Thread.currentThread().getContextClassLoader().loadClass(className);
        } catch (Exception e) {
            // ignore
        }
        if (clazz == null) {
            clazz = Class.forName(className);
        }
        return clazz;
    }

    public static boolean isNoArgInConstructor() {
        return isNoArgInConstructor;
    }

    private static void setCacheInfo(String key, Log logger) {
        if (logger == null) return;
        if (key == null || "".equals(key.trim())) return;
        Map<String, Log> map = threadLocal.get();
        if (null == map) map = new HashMap<>();
        map.put(key, logger);
        threadLocal.set(map);
    }

    private static Log getCacheInfo(String key) {
        Map<String, Log> map = threadLocal.get();
        if (null == map) {
            return null;
        }
        return map.get(key);
    }
}
